
/* 
 * This file is part of FreeBMS - an open source battery management system
 *
 * Structure Functions
 * 
 * NOTE: This code is designed to be compiled using SOURCEBOOST (http://www.sourceboost.com)
 */
 
/* This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <system.h>
#include "struct.h"
#include "serial.h"
#include "canbus.h"
#include "dtc.h"

struct cell_data *first_cell; 		// First cell in list
struct mesg_data *first_mesg; 		// First message in list

// Insert a cell into the linked list
struct cell_data *insert_cell(BYTE id, BYTE shunting, unsigned int volt) {
	struct cell_data *new_cell;
	struct cell_data *tmp_ptr;

	// Create a new record
	new_cell = (struct cell_data *) alloc(sizeof(cell_data));

	// Verify that record was successfully created
	if(new_cell != NULL) {
		new_cell->id = id;
		new_cell->shunting = 0;
		new_cell->comm_errors = 0;
		new_cell->cell_errors = 0;
		new_cell->voltage = volt;

		// Zero out the next cell pointer
		new_cell->next = NULL;

		// Link the new record into the list
		if(first_cell == 0)
			first_cell = new_cell;
		else {
			for(tmp_ptr = first_cell; tmp_ptr; tmp_ptr = tmp_ptr->next) {
				if(tmp_ptr->next == NULL) {
					tmp_ptr->next = new_cell;
					break;
				}
			}
		}
 
		// Return reference to new cell
		return new_cell;
	} else {
		// If we got here, we ran out of memory
		error_memfault = true;
		return NULL;
	}
}

// Find a cell record based on provided ID
struct cell_data *find_cell(BYTE id) {
	struct cell_data *tmp_cell;

	// Loop through cells to find ID
	for(tmp_cell = first_cell; tmp_cell; tmp_cell = tmp_cell->next) {
		if(tmp_cell->id == id) {
			// Return the cell pointer 
			return tmp_cell;
		}
	}

	// Else return NULL, record not found
	return NULL;
}

// Update shunting for chip
struct cell_data *update_shunting(BYTE id, BYTE shunt) {
	struct cell_data *tmp_cell;

	// Find the cell
	tmp_cell = find_cell(id);
	
	// Verify that we found the record
	if(tmp_cell != NULL) {
		// Update the values of the cell
		tmp_cell->shunting = shunt;
	} else {
		tmp_cell = insert_cell(id, shunt, 0);	
	}
	
	return tmp_cell;
}

// Update voltage for chip
struct cell_data *update_voltage(BYTE id, BYTE shunting, unsigned int volt) {
	struct cell_data *tmp_cell;

	// Find the cell
	tmp_cell = find_cell(id);
	
	if(tmp_cell != NULL) {
		// Update the values of the cell
		tmp_cell->shunting = shunting;
		tmp_cell->voltage = volt;
	} else {
		tmp_cell = insert_cell(id, 0, volt);	
	}
	
	return tmp_cell;
}

// Insert a cell into the linked list
struct mesg_data *insert_mesg(BYTE type, unsigned short id, BYTE dest, BYTE rxblock, BYTE length, BYTE *buffer) {
	struct mesg_data *new_ptr;
	struct mesg_data *tmp_ptr;
	BYTE record = 0;
	BYTE checksum = 0;
	
	// Create a new record
	new_ptr = (struct mesg_data *) alloc(sizeof(mesg_data));

	// Verify that new_ptr was successfully created
	if(new_ptr != NULL) {
		new_ptr->mesg_id = id;
		new_ptr->type = type;
		new_ptr->dest = dest;
		new_ptr->rxblock = rxblock;

		// If type is CANBUS and we have room, include checksum
		if(type == MESG_CANBUS && length <= 6) {
			length += 1;
			checksum = id;
		}

		new_ptr->length = length;

		// Loop from 1 to length and copy information over
		for(record = 0; record < length; ++record) {
			new_ptr->data[record] = buffer[record];
			checksum += buffer[record];
		}

		// If message is CANBUS and has room, add checksum to end
		if(type == MESG_CANBUS && length <= 7) {
			checksum &= 0xFF;
			new_ptr->data[length] = checksum;
		}

		// Zero out the next pointer
		new_ptr->next = NULL;
			
		// Link the new record into the list
		if(first_mesg == 0)
			first_mesg = new_ptr;
		else {
			for(tmp_ptr = first_mesg; tmp_ptr; tmp_ptr = tmp_ptr->next) {
				if(tmp_ptr->next == NULL) {
					tmp_ptr->next = new_ptr;
					break;
				}
			}
		}

		// Return reference to new record
		return new_ptr;
	} else {
		// If we got here, we ran out of memory
		error_memfault = true;
		return NULL;
	}
}